﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SimpleGameData.GameObjects;
using System.Diagnostics;
using SimpleGame.Utils;

namespace SimpleGame.GameObjects.Particles
{
    public class Particle
    {
        #region Fields & Auto-Properties

        #region Appearance

        // The texture used of this game object
        public Texture2D Texture { get; set; }
        // color values with start and end values so particle color can change over time
        public float ColorRed { get; set; }
        public float ColorRedStart { get; set; }
        public float ColorRedEnd { get; set; }
        public float ColorGreen { get; set; }
        public float ColorGreenStart { get; set; }
        public float ColorGreenEnd { get; set; }
        public float ColorBlue { get; set; }
        public float ColorBlueStart { get; set; }
        public float ColorBlueEnd { get; set; }
        public float AlphaValue { get; set; }
        public float AlphaValueStart { get; set; }
        public float AlphaValueEnd { get; set; }

        #endregion

        #region Position, Direction and Movement

        // The position of this particle
        public Vector2 Position { get; set; }
        // The direction vector calculated out of the emissionAngle
        // to let the particle fly into the right direction
        private Vector2 direction;
        // The angle in which the particles can fly out of the emitter
        public float EmissionAngle { get; set; }
        // The velocity of this particle
        public float Velocity { get; set; }
        // The amount of speed this particle gains per update
        public float Acceleration { get; set; }

        #endregion

        #region Rotation and Scale

        // The rotation of this particle
        public float Rotation { get; set; }
        // The speed at which the particle rotates
        public float RotationSpeed { get; set; }
        // true = rotation direction is randomed between clockwise and counter-clockwise
        // false = rotation is not randomed and isRotationClockwise is used instead
        private bool isRotationDirectionRandom;
        // The The direction in which this particle rotates.
        private bool isRotationClockwise;
        // The direction in which this particle rotates dependend on the bool RotationClockwise.
        // 1 = clockwise | -1 = counter-clockwise
        private float rotationDirection;
        // The scale of this particle
        public float Scale { get; set; }
        // The increase or decrease of scale per update
        public float ScaleShift { get; set; }

        #endregion

        #region Life

        // true = particle is alive, drawn and updated
        // false = particle is not alive and can be reused
        public bool IsAlive { get; set; }
        // The amount of time the particle can live
        public float LifeTime { get; set; }
        // The amount of time the particle is already alive
        private float lifeTimer;

        #endregion

        #endregion

        #region Properties

        public bool IsRotationDirectionRandom
        {
            get { return isRotationDirectionRandom; }
            set 
            { 
                isRotationDirectionRandom = value;
                if (isRotationDirectionRandom == true)
                {
                    bool random = RandomGenerator.RandomBool();
                    // set rotationDirection depending on the bool value for easy use in calculations
                    if (random == true)
                    {
                        IsRotationClockWise = true;
                    }
                    else
                    {
                        IsRotationClockWise = false;
                    }
                }
            }
        }

        /// <summary>
        /// true = rotation is clockwise
        /// false = rotation is counter-clockwise
        /// </summary>
        public bool IsRotationClockWise
        {
            get { return isRotationClockwise; }
            set
            {
                isRotationClockwise = value;
                // set rotationDirection depending on the bool value for easy use in calculations
                if (isRotationClockwise == true)
                {
                    rotationDirection = 1f;
                }
                else
                {
                    rotationDirection = -1f;
                }
            }
        }

        /// <summary>
        /// The Center of the particles texture. i.e. used as rotation origin.
        /// </summary>
        public Vector2 SpriteCenter
        {
            get
            {
                return new Vector2(
                  (float)Texture.Width / 2f,
                  (float)Texture.Height / 2f);
            }
        }

        #endregion

        #region Constructors

        public Particle() 
        {
            IsAlive = false;
            lifeTimer = 0.0f;
        }

        #endregion

        #region General Methods

        public void ResetAndSpawn()
        {
            ColorRed = ColorRedStart;
            ColorGreen = ColorGreenStart;
            ColorBlue = ColorBlueStart;
            AlphaValue = AlphaValueStart;

            // Calculate the angle in Radians from angle in Degrees
            float emissionAngleRadians = MathHelper.ToRadians(EmissionAngle);
            // Calculate the movement direction of the particle from the position and angle
            direction = Vector2.Transform(Position, Matrix.CreateRotationZ(emissionAngleRadians));

            IsAlive = true;
        }

        #endregion

        #region Update & Draw Method

        #region Color Update

        public void UpdateColor(float elapsedTime)
        {
            float shiftPerUpdate = 0;

            // red color shift
            shiftPerUpdate = Math.Abs(ColorRedStart - ColorRedEnd) / (LifeTime / elapsedTime);
            if (ColorRedStart > ColorRedEnd)
            {
                ColorRed -= shiftPerUpdate;
            }
            else
            {
                ColorRed += shiftPerUpdate;
            }

            // green color shift
            shiftPerUpdate = Math.Abs(ColorGreenStart - ColorGreenEnd) / (LifeTime / elapsedTime);
            if (ColorGreenStart > ColorGreenEnd)
            {
                
                ColorGreen -= shiftPerUpdate;
            }
            else
            {
                ColorGreen += shiftPerUpdate;
            }

            // blue color shift
            shiftPerUpdate = Math.Abs(ColorBlueStart - ColorBlueEnd) / (LifeTime / elapsedTime);
            if (ColorBlueStart > ColorBlueEnd)
            {
                ColorBlue -= shiftPerUpdate;
            }
            else
            {
                ColorBlue += shiftPerUpdate;
            }

            // alpha color shift
            shiftPerUpdate = Math.Abs(AlphaValueStart - AlphaValueEnd) / (LifeTime / elapsedTime);
            if (AlphaValueStart > AlphaValueEnd)
            {
                AlphaValue -= shiftPerUpdate;
            }
            else
            {
                AlphaValue += shiftPerUpdate;
            }
        }

        #endregion

        public void Update(GameTime gameTime)
        {
            // only update if particle is alive
            if (IsAlive)
            {
                // get elapsed time since last update
                float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
                // add elapsed time to lifetimer
                lifeTimer += elapsed;

                // update the color values of the particle
                UpdateColor(elapsed);
                //Debug.WriteLine(AlphaValue);

                // rotate the particle
                Rotation += RotationSpeed * rotationDirection;

                // Move particle with normalized direction vector
                if (direction != Vector2.Zero)
                {
                    direction.Normalize();
                    Position += direction * Velocity * elapsed;
                }

                // Add acceleration
                Velocity += Acceleration;

                // Increase/Decrease the scale
                Scale += ScaleShift;

                // Check if lifetime of the particle is up, then "kill" particle
                // and reset it's life timer so the particle can be reused
                if (lifeTimer >= LifeTime)
                {
                    IsAlive = false;
                    lifeTimer = 0.0f;
                }
            }
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            // only draw if particle is alive
            if (IsAlive)
            {
                spriteBatch.Draw(
                    Texture,
                    Position,
                    null,
                    new Color((byte)ColorRed, (byte)ColorGreen, (byte)ColorBlue) * AlphaValue,
                    Rotation,
                    SpriteCenter,
                    Scale,
                    SpriteEffects.None,
                    0);
            }
        }

        #endregion
    }
}
